import { omit } from "lodash-es";
import type { ModalMethods, ModalProps } from "./typing";
import Modal from "./components/Modal";
import ModalWrapper from "./components/ModalWrapper";
import ModalClose from "./components/ModalClose";
import ModalFooter from "./components/ModalFooter";
import ModalHeader from "./components/ModalHeader";
import { useFullScreen } from "./hooks/useModalFullScreen";
import { isFunction } from "@/utils/is";
import { deepMerge, nextTick } from "@/utils";
import { useEffect, useMemo, useRef, useState } from "react";
import { childrenToMap } from "@/utils/helper/tsxHelper";
import { BasicPropsType, BasicPropsDefault } from "./props";

interface ComponentProps extends ComponentNode {
	register: Fn;
	ok?: Fn;
	cancel?: Fn;
}

const BasicModal = (props: BasicPropsType & ComponentProps) => {
	// fix React18 "defaultProps" problem
	props = Object.assign({}, { ...BasicPropsDefault, ...props });

	const slots = childrenToMap(props.children);

	const [openRef, setOpenRef] = useState<boolean>(false);
	const [propsRef, setPropsRef] = useState<Partial<ModalProps> | null>(null);
	const modalWrapperRef = useRef<any>(null);
	const prefixCls = "basic-modal";
	const uid = useRef(0);
	// modal   Bottom and top height
	const [extHeightRef, setExtHeightRef] = useState<number>(0);
	const modalMethods: ModalMethods = {
		setModalProps,
		emitOpen: undefined,
		redoModalHeight: () => {
			nextTick(() => {
				if (modalWrapperRef.current) {
					modalWrapperRef.current.setModalHeight();
				}
			});
		}
	};

	// Custom title component: get title
	const getMergeProps = useMemo((): Recordable => {
		return {
			...props,
			...propsRef
		};
	}, [propsRef]);

	const { handleFullScreen, getWrapClassName, fullScreenRef } = useFullScreen({
		modalWrapperRef: modalWrapperRef.current,
		extHeightRef,
		wrapClassName: getMergeProps.wrapClassName
	});

	// modal component does not need title and origin buttons
	const getProps = useMemo((): Recordable => {
		const opt = {
			...getMergeProps,
			open: openRef,
			okButtonProps: undefined,
			cancelButtonProps: undefined,
			title: undefined
		};
		return {
			...opt,
			wrapClassName: getWrapClassName
		};
	}, [getMergeProps, openRef, getWrapClassName]);

	const getBindValue = useMemo((): Recordable => {
		const attr: any = {
			...getMergeProps,
			open: openRef
		};
		attr.wrapClassName = `${attr?.wrapClassName || ""} ${getWrapClassName}`;
		if (fullScreenRef) return omit(attr, ["height", "title"]);
		return omit(attr, "title");
	}, [getMergeProps, openRef]);

	const getWrapperHeight = useMemo(() => {
		if (fullScreenRef) return undefined;
		return getProps.height;
	}, [fullScreenRef]);

	// 取消事件
	async function handleCancel(e: Event) {
		e?.stopPropagation();
		// 过滤自定义关闭按钮的空白区域
		if ((e.target as HTMLElement)?.classList?.contains(`${prefixCls}-close--custom`)) return;
		if (props.closeFunc && isFunction(props.closeFunc)) {
			const isClose: boolean = await props.closeFunc();
			setOpenRef(!isClose);
			return;
		}
		setOpenRef(false);
		props.cancel && props.cancel(e);
	}

	/**
	 * @description: 设置modal参数
	 */
	function setModalProps(props: Partial<ModalProps>): void {
		// Keep the last setModalProps
		setPropsRef(deepMerge(propsRef || ({} as any), props));
		if (Reflect.has(props, "open")) setOpenRef(!!props.open);
		if (Reflect.has(props, "defaultFullscreen")) fullScreenRef.current = !!props.defaultFullscreen;
	}

	function handleOk(e: Event) {
		props.ok && props.ok(e);
	}

	function handleExtHeight(height: number) {
		setExtHeightRef(height);
	}

	function handleTitleDbClick(e) {
		if (!props.canFullscreen) return;
		e.stopPropagation();
		handleFullScreen(e);
	}

	/** Effect */

	useEffect(() => {
		uid.current = parseInt(Math.random() * 10000);
		props.register && props.register(modalMethods, uid.current);
		return () => {
			// clean
			setOpenRef(false);
			setPropsRef(null);
			modalWrapperRef.current = null;
			uid.current = 0;
			setExtHeightRef(0);
			modalMethods.emitOpen = undefined;
		};
	}, []);

	useEffect(() => {
		setOpenRef(!!props.open);
		fullScreenRef.current = !!props.defaultFullscreen;
	}, [props.open, props.defaultFullscreen]);

	useEffect(() => {
		modalMethods && modalMethods.emitOpen?.(openRef, uid.current);
		nextTick(() => {
			if (props.scrollTop && openRef && modalWrapperRef.current) {
				modalWrapperRef.current.scrollTop();
			}
		});
	}, [openRef]);

	/** Render */
	const renderCloseSlot = () => {
		return slots.closeIcon ? (
			slots.closeIcon
		) : (
			<ModalClose
				canFullscreen={getProps.canFullscreen}
				fullScreen={fullScreenRef}
				cancel={handleCancel}
				fullscreen={handleFullScreen}
			/>
		);
	};

	const renderTitleSlot = () => {
		return slots.title ? (
			slots.title
		) : (
			<ModalHeader helpMessage={getProps.helpMessage} title={getMergeProps.title} dblclick={handleTitleDbClick} />
		);
	};
	const renderFooterSlot = () => {
		return slots.footer ? slots.footer : <ModalFooter {...getBindValue} ok={handleOk} cancel={handleCancel} />;
	};

	return (
		<Modal
			{...getBindValue}
			onCancel={handleCancel}
			title={renderTitleSlot()}
			closeIcon={renderCloseSlot()}
			footer={renderFooterSlot()}
		>
			<ModalWrapper
				useWrapper={getProps.useWrapper}
				fullScreen={fullScreenRef}
				loading={getProps.loading}
				loadingTip={getProps.loadingTip}
				minHeight={getProps.minHeight}
				height={getWrapperHeight}
				open={openRef}
				{...omit(getProps.wrapperProps, "open", "height", "modalFooterHeight")}
				extHeight={handleExtHeight}
			>
				{slots.default}
			</ModalWrapper>
		</Modal>
	);
};

export default BasicModal;
